import itertools
import lightgbm
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
import time
from concurrent.futures import ProcessPoolExecutor, as_completed

# 第二次网格搜索在第一次网格搜索的基础上进行（非搜索参数采用第一次网格搜索的结果）
# 经运行得出的最佳的三组参数为:
# ('gbdt', 'mape', 'mape', 40, 20, 180, 1, 0.7, 0.8, 5, -1, 2) wmape=0.6770930150976581
# ('gbdt', 'mape', 'mape', 40, 20, 180, 1, 0.7, 0.7, 5, -1, 2) wmape=0.6775527412658426
# ('gbdt', 'mape', 'mape', 40, 20, 200, 1, 0.7, 0.7, 5, -1, 2) wmape=0.6787400066234566


def train_cv(params, x, y, nflod=5):
    # 五折交叉验证
    wmape_ls = []
    lgb = lightgbm.LGBMModel(**params)
    for i in range(nflod):
        train_x, test_x, train_y, test_y = train_test_split(x, y, test_size=0.3)
        # 模型建立与训练
        lgb.fit(train_x, train_y)
        # 预测数据集
        pred_y = lgb.predict(test_x)
        # 计算评价指标
        wmape1 = sum(abs(test_y - pred_y))
        wmape2 = sum(test_y)
        wmape_ls.append(wmape1 / wmape2)
    return np.mean(wmape_ls)


if __name__ == '__main__':
    # 导入数据集
    data = pd.read_csv('train_wide_data2.csv', converters={'sale_date': pd.to_datetime})
    x = data.drop('sales_qty', axis=1)
    y = data['sales_qty']

    # 参数
    param_grid = {
        'boosting_type': ['gbdt'],  # 设置提升类型
        'objective': ['mape'],  # 目标函数
        'metric': ['mape'],  # 评估函数
        'num_leaves': [40],  # 叶子节点数
        'min_data_in_leaf': [20],  # 每个叶节点的最少样本数量
        'n_estimators': [20, 40, 60, 80, 100, 120, 140, 160, 180, 200],  # 给出了boosted trees 的数量
        'learning_rate': [0.2, 0.4, 0.6, 0.8, 1],  # 学习速率
        'feature_fraction': [0.7],  # 建树的特征选择比例
        'bagging_fraction': [0.7, 0.8],  # 建树的样本采样比例
        'bagging_freq': [5],  # k 意味着每 k 次迭代执行bagging
        'verbose': [-1],  # <0 显示致命的, =0 显示错误 (警告), >0 显示信息
        'num_threads': [2],  # 给出了lightgbm 的线程数
    }
    t1 = time.time()
    all_params = [dict(zip(param_grid.keys(), v)) for v in itertools.product(*param_grid.values())]
    result_ls = dict()
    with ProcessPoolExecutor(max_workers=3) as executor:
        futures = {}
        for params in all_params:
            job = executor.submit(train_cv, params, x, y, 5)
            futures[job] = params
        for job in as_completed(futures):
            avg_wmape = job.result()
            params = futures[job]
            result_ls[tuple(params.values())] = avg_wmape
    print('共耗时：{}'.format(time.time() - t1))